import type { SerialTaskExecuteContext } from '../handler'
import type { BusinessParams, PrivateCustomEventName, SystemConfig, TtsWebSocketResponse } from '../types'
import CryptoJS from 'crypto-js'

import { SerialHandler } from '../handler'
import { createEventBus, encodeText } from '../utils'

const $bus = createEventBus<PrivateCustomEventName>()

const defaultBusinessParams: BusinessParams = {
  aue: 'raw',
  sfl: 1,
  auf: 'audio/L16;rate=16000',
  vcn: 'xiaoyan',
  speed: 50,
  volume: 50,
  pitch: 50,
  bgs: 0,
  tte: 'UTF8',
  reg: '2',
  rdn: '0',
}

class TtsRequest extends SerialHandler<string, string> {
  private systemConfig!: SystemConfig

  private businessParams!: BusinessParams

  private webSockeSet: Set<WebSocket> = new Set()

  public initProperty(systemConfig: SystemConfig, businessParams: Partial<BusinessParams>): void {
    this.systemConfig = systemConfig
    this.businessParams = { ...defaultBusinessParams, ...businessParams }
  }

  public execute(
    context: SerialTaskExecuteContext<string, string>,
  ): void {
    if (context.isLastExecute) {
      this.taskCompletedCallback()
      return
    }

    const webSocketInstance = new WebSocket(this.generateRequestUrl())
    this.webSockeSet.add(webSocketInstance)
    webSocketInstance.addEventListener('open', async () => {
      webSocketInstance.send(JSON.stringify(this.generateRequestParams(context.taskItem.original!)))

      webSocketInstance.addEventListener('message', (response: MessageEvent<string>) => {
        const result = JSON.parse(response.data) as TtsWebSocketResponse

        if (result.code === 0) {
          if (result.data?.audio) {
            this.forwardToHandler(result.data.audio)
          }
          if (result.data?.status === 2) {
            this.webSockeSet.delete(webSocketInstance)
            webSocketInstance.close()

            this.taskCompletedCallback()
          }
        }
      })
    })
  }

  private generateRequestParams(text: string): any {
    return {
      common: {
        app_id: this.systemConfig.APPID,
      },
      business: {
        ...this.businessParams,
      },
      data: {
        status: 2,
        text: encodeText(text, this.businessParams.tte!),
      },
    }
  }

  private generateRequestUrl(): string {
    const apiKey = this.systemConfig.API_KEY
    const apiSecret = this.systemConfig.API_SECRET
    let url = 'wss://tts-api.xfyun.cn/v2/tts'
    const { host } = window.location
    const date = (new Date() as any).toGMTString()
    const algorithm = 'hmac-sha256'
    const headers = 'host date request-line'
    const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/tts HTTP/1.1`
    const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
    const signature = CryptoJS.enc.Base64.stringify(signatureSha)
    const authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
    const authorization = btoa(authorizationOrigin)
    url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
    return url
  }

  protected onFinish(): void {
    $bus.emit('_ttsRequestFinish')
    if (this.webSockeSet && this.webSockeSet.size) {
      this.webSockeSet.forEach(ws => ws.close())
      this.webSockeSet.clear()
    }
    this.webSockeSet = new Set()
  }
}

export default TtsRequest
